#!/usr/bin/env python
# -*- coding:utf8 -*-
# uncompyle6 version 2.15.1
# Python bytecode 2.7 (62211)
# Decompiled from: Python 2.7.10 (default, Jul  1 2017, 13:36:56) 
# [GCC 4.4.6 20110731 (Red Hat 4.4.6-4)]
# Embedded file name: ./appengine/api/views.py
# Compiled at: 2017-11-16 15:44:28
"""
Tencent is pleased to support the open source community by making 蓝鲸智云(BlueKing) available.
Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.

RESTful view classes for presenting BK_PaaS API objects.
"""
import json, re, logging, requests
from django.http import HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404
from django.views.generic import View
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from rest_framework import status
from rest_framework.viewsets import GenericViewSet
from rest_framework.response import Response
from api import authentication, models, permissions
from api.serializers import BkAppSerializer, BkAppEventSerializer
from api import http
from api.utils import assign_servers, check_rabbitmq, apply_mq_res
logger = logging.getLogger(__name__)

class HealthCheckView(View):
    """
    api {get} /healthz 健康状态
    apiName HealthCheck
    apiGroup App
    apiVersion 1.0.0
    apiSuccessExample {json} Success-Response:
        HTTP/1.1 200 OK
            "OK"
    """

    def get(self, request):
        try:
            import django.db
            with django.db.connection.cursor() as (c):
                c.execute('SELECT 0')
        except django.db.Error as e:
            logger.exception(u'Database health check failed! 数据库连接存在问题 %s' % str(e))
            return HttpResponse(u'Database health check failed! 数据库连接存在问题 %s' % str(e), status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        try:
            bk_servers = models.BkServer.objects.filter(is_active=True)
            bk_server_count = bk_servers.count()
            if bk_server_count == 0:
                return HttpResponse(u'没有注册并激活可用的agent服务器', status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            for bk_server in bk_servers:
                ip_address, ip_port, sid, token = (
                 bk_server.ip_address,
                 bk_server.ip_port,
                 bk_server.s_id,
                 bk_server.token)
                try:
                    url = ('http://{}:{}/v1/app/healthz').format(ip_address, ip_port)
                    headers = http._gen_header(sid, token)
                    resp = requests.get(url=url, headers=headers, timeout=10)
                except Exception as e:
                    message = u'PaaSAgent healthz FAIL! 请求异常! url=%s, sid=%s, token=%s, error=%s' % (url, sid, token, e)
                    return HttpResponse(message, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

                status_code = resp.status_code
                if status_code != status.HTTP_200_OK:
                    message = u'PaaSAgent healthz FAIL! 状态异常! url=%s, sid=%s, token=%s, status=%s' % (
                     url, sid, token, status_code)
                    return HttpResponse(message, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
                result = resp.json()
                if result.get('error') != 0:
                    message = u'PaaSAgent healthz FAIL!: sid/token error! url=%s, sid=%s, token=%s, resp=%s' % (
                     url, sid, token, result)
                    return HttpResponse(message, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        except Exception as e:
            return HttpResponse(u'PaaSAgent health check failed! internal ERROR: %s' % str(e), status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        return HttpResponse('OK')

    head = get


class AgentHealthCheckView(View):
    """
    api {get} /v1/agents/healthz Agent状态
    apiName AgentHealthCheck
    apiGroup App
    apiVersion 1.0.0
    apiSuccessExample {json} Success-Response:
        HTTP/1.1 200 OK
            {
                "agents": {
                    "s_id1": { "code": 0}
                    "s_id2": { "code": 1}
                    }
            }
    """

    def get(self, request):
        try:
            _agents = {}
            server_id = request.GET.get('server_id')
            bk_servers = models.BkServer.objects.filter(id=int(server_id))
            for bk_server in bk_servers:
                ip_address, ip_port, sid, token = (
                 bk_server.ip_address, bk_server.ip_port,
                 bk_server.s_id, bk_server.token)
                _, result = http.http_get(('http://{}:{}/v1/app/healthz').format(ip_address, ip_port), sid, token, {})
                if isinstance(result, dict) and result.get('error') == 0:
                    _agents[str(sid)] = {'code': 0,'mac': result.get('mac', '')}
                else:
                    _agents[str(sid)] = {'code': 1}

        except Exception as e:
            return JsonResponse({'msg': 'health check failed: %s' % e}, status=status.HTTP_503_SERVICE_UNAVAILABLE)

        return JsonResponse({'agents': _agents})


class ServiceViewSet(GenericViewSet):

    def check_status(self, *args, **kwargs):
        try:
            service_name = self.kwargs.get('service_name', '')
            params = self.request.query_params
            if service_name == 'rabbitmq':
                server_id = params.get('server_id', '')
                if not server_id:
                    return JsonResponse({'code': 1,'msg': u'缺少有效的server_id'})
                server_data = models.ThirdServer.objects.get(id=server_id).server_data
                server_info = {'server_ip': server_data.get('ip_address'),
                   'server_port': server_data.get('ip_port')
                   }
                admin_account = {'name': server_data.get('username'),
                   'password': server_data.get('password')
                   }
                ret, data = check_rabbitmq(server_info, admin_account)
                if ret:
                    return JsonResponse({'code': 0,'msg': ''})
                return JsonResponse({'code': 1,'msg': data})
            else:
                return JsonResponse({'code': 0,'msg': ''})
        except Exception as e:
            return JsonResponse({'code': 1,'msg': str(e)})


class BaseAppViewSet(GenericViewSet):
    authentication_classes = (
     authentication.AppAuthentication,)
    permission_classes = (permissions.IsAppUser,)

    def get_app(self):
        app = get_object_or_404(models.BkApp, app_code=self.kwargs['app_code'])
        self.check_object_permissions(self.request, app)
        return app


class BaseAgentViewSet(GenericViewSet):
    authentication_classes = (
     authentication.AgentAuthentication,)
    permission_classes = (permissions.IsAgent,)


class AppViewSet(BaseAppViewSet):
    serializer_class = BkAppSerializer

    def init(self, request, *args, **kwargs):
        """
        api {post} /v1/apps/init App初始化
        apiName AppInit
        apiGroup App
        apiVersion 1.0.0
        apiParam {String} app_code
        apiParam {String} name
        apiParam {String} app_lang
        apiSuccessExample {json} Success-Response:
            HTTP/1.1 200 OK
               {
                 "id": 8,
                 "token": "2b408532-9140-4e82-adde-3f092b467189",
                 "name": "开发框架4",
                 "logo": "",
                 "app_code": "app-tem1plat1e",
                 "app_lang": "Python",
                 "app_type": "1",
                 "is_active": true,
                 "created_at": "2016-03-29T10:04:30.504368Z",
                 "updated_at": "2016-03-29T10:04:30.504460Z"
               }
        """
        app_code = request.data.get('app_code')
        bk_app, created = models.BkApp.objects.update_or_create(app_code=app_code, defaults=request.data)
        if bk_app and created:
            models.BkAppToken.objects.create(bk_app=bk_app)
        serializer = BkAppSerializer(bk_app)
        return Response(serializer.data)

    def info(self, *args, **kwargs):
        """
        api {post} /v1/apps/<app_code>/info App信息
        apiName AppInfo
        apiGroup App
        apiVersion 1.0.0
        apiSuccessExample {json} Success-Response:
            HTTP/1.1 200 OK
               {
                 "id": 8,
                 "token": "2b408532-9140-4e82-adde-3f092b467189",
                 "name": "开发框架4",
                 "logo": "",
                 "app_code": "app-tem1plat1e",
                 "app_lang": "Python",
                 "app_type": "1",
                 "is_active": true,
                 "created_at": "2016-03-29T10:04:30.504368Z",
                 "updated_at": "2016-03-29T10:04:30.504460Z"
                 "app_envs": {
                   "test": {
                     "VCS_PATH": "git://xxx",
                     "VCS_PASSWORD": "XXXX",
                     "VCS_USERNAME": "XX" },
                   "prod": {
                     "VCS_PATH": "git://xxx",
                     "VCS_PASSWORD": "XXXX",
                     "VCS_USERNAME": "XX" },
                   }
                 },
               }
        """
        bk_app = self.get_app()
        serializer = BkAppSerializer(bk_app)
        return Response(serializer.data)


class AppReleasesViewSet(BaseAppViewSet):

    def releases(self, request, *args, **kwargs):
        """
        api {post} /v1/apps/<app_code>/releases App发布
        apiName AppRelease
        apiGroup App
        apiVersion 1.0.0
        apiParam {String} mode (prod/test)
        apiParam {Dict} envs
        apiSuccessExample {json} Success-Response:
            HTTP/1.1 200 OK
               {
                    "event_id": "0103c74f6b87490289bf2b93d9922564",
               }
        """
        mode = request.data.get('mode')
        triggers = request.data.get('triggers', {})
        envs = request.data.get('envs', {})
        bk_app = self.get_app()
        app_code = bk_app.app_code
        bk_app_event = models.BkAppEvent.objects.create(bk_app=bk_app, event_type=('app.{}.deploy').format(mode), status='READY')
        event_id = bk_app_event.id
        if triggers.get('is_use_celery'):
            try:
                rabbitmq_id = request.data.get('services').get('rabbitmq')[0]
                server_data = models.ThirdServer.objects.get(id=rabbitmq_id).server_data
                mq_pwd = str(bk_app.token)[:8]
                vhost = 'test_%s' % app_code if mode == 'test' else 'prod_%s' % app_code
                envs['BK_BROKER_URL'] = 'amqp://%s:%s@%s:5672/%s' % (
                 app_code, mq_pwd, server_data.get('ip_address'), vhost)
                envs['IS_USE_CELERY'] = 'true'
                if triggers.get('is_use_celery_beat'):
                    envs['IS_USE_CELERY_BEAT'] = 'true' if 1 else 'false'
                    server_info = {'server_ip': server_data.get('ip_address'),
                       'server_port': server_data.get('ip_port')
                       }
                    admin_account = {'name': server_data.get('username'),
                       'password': server_data.get('password')
                       }
                    user_account = {'name': app_code,
                       'password': mq_pwd
                       }
                    ret, data = apply_mq_res(server_info, admin_account, user_account, vhost)
                    return ret or Response({'event_id': event_id,'error_code': 20300,'msg': data}, status=status.HTTP_400_BAD_REQUEST)
            except Exception as e:
                return Response({'event_id': event_id,'error_code': 20300,'msg': str(e)}, status=status.HTTP_400_BAD_REQUEST)

        for key, value in envs.iteritems():
            models.BkAppEnv.objects.update_or_create(bk_app=bk_app, mode=mode, key=key, defaults={'key': key,'value': value})

        _success, _msg = assign_servers(self.kwargs['app_code'], mode)
        if not _success:
            bk_app_event.status, bk_app_event.data = 'FAILURE', _msg
            bk_app_event.save()
            return Response({'event_id': event_id,'msg': _msg}, status=status.HTTP_400_BAD_REQUEST)
        if request.data.get('is_saas', False):
            saas_settings = request.data.get('saas_settings', {})
            saas_settings['is_saas'] = 'true'
        else:
            saas_settings = {'is_saas': 'false'}
        ret, data = bk_app.releases(event_id, mode, envs, 'online', deploy_token=request.data.get('deploy_token', ''), deploy_vars=request.data.get('deploy_vars', {}), saas_settings=saas_settings)
        if not ret:
            return Response({'event_id': event_id,'msg': u'agent http服务异常'}, status=status.HTTP_400_BAD_REQUEST)
        error_code = data.get('error', 1)
        if error_code == 0:
            return Response({'event_id': event_id})
        return Response({'event_id': event_id,'error_code': error_code,'msg': data.get('msg')}, status=status.HTTP_400_BAD_REQUEST)

    def destroy(self, *args, **kwargs):
        """
        api {delete} /v1/apps/<app_code>/releases App下线
        apiName AppOffline
        apiGroup App
        apiVersion 1.0.0
        apiParam {String} mode (prod/test)
        apiSuccessExample {json} Success-Response:
            HTTP/1.1 200 OK
               {
                    "event_id": "0103c74f6b87490289bf2b93d9922564",
                    "status": "READY",
                    "msg": "msgmsg",
               }
        """
        mode = self.request.data.get('mode')
        bk_app = self.get_app()
        bk_app_event = models.BkAppEvent.objects.create(bk_app=bk_app, event_type=('app.{}.offline').format(mode), status='READY')
        category = 'app' if mode == 'prod' else 'tapp'
        count = bk_app.bkserver_set.filter(category=category).count()
        event_id = bk_app_event.id
        if count < 1:
            return Response({'event_id': event_id,'msg': 'no servers avaliable. you need to install PaasAgent on at least one server and register the server to PaaS'
               }, status=status.HTTP_400_BAD_REQUEST)
        bk_app.releases(event_id, mode, {}, 'offline')
        return Response({'event_id': bk_app_event.id})


class AppLogViewSet(BaseAppViewSet):

    def logs(self, *args, **kwargs):
        """
        api {get} /v1/apps/<app_code>/events/<event_id>/logs App事件日志
        apiName AppEventLog
        apiGroup App
        apiVersion 1.0.0
        apiSuccessExample {json} Success-Response:
            HTTP/1.1 200 OK
               {
                    "status": "SUCCESS",
                    "logs": "部署日志",
                    "event_type": "app.deploy",
                    "app_code": "app-template",
               }
        """
        event_id = self.kwargs['event_id']
        bk_app_event = models.BkAppEvent.objects.get(id=event_id)
        serializer = BkAppEventSerializer(bk_app_event)
        return Response(serializer.data)


class AppEventLogViewSet(BaseAgentViewSet):

    def create(self, *args, **kwargs):
        """
        api {post} /v1/apps/<app_code>/events/<event_id> App事件日志记录
        apiName AppEventLogCreate
        apiGroup App
        apiVersion 1.0.0
        apiParam {String} status (READY/PENDING/SUCCESS/FAILURE)
        apiParam {Text} log
        apiSuccessExample {json} Success-Response:
            HTTP/1.1 200 OK
               {
                    "status": "SUCCESS",
                    "logs": "部署日志",
               }
        """
        event_id = self.kwargs['event_id']
        status = self.request.data.get('status')
        log = self.request.data.get('log')
        bk_app_event = models.BkAppEvent.objects.get(id=event_id)
        models.BkAppEventLog.objects.create(bk_app_event=bk_app_event, log=log)
        if status in ('READY', 'PENDING', 'FAILURE', 'SUCCESS'):
            bk_app_event.status = status
            bk_app_event.save()
        serializer = BkAppEventSerializer(bk_app_event)
        return Response(serializer.data)


class AgentRegistryView(View):

    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        return super(AgentRegistryView, self).dispatch(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        agent_ip = request.POST.get('agent_ip', '')
        agent_port = request.POST.get('agent_port', '4245')
        mode = request.POST.get('mode', 'test')
        web_port = request.POST.get('web_port', '8085')
        if not agent_ip:
            return JsonResponse({'msg': u'agent_ip不能为空'}, status=400)
        ip_pattarn = re.compile('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')
        if not ip_pattarn.match(agent_ip):
            return JsonResponse({'msg': u'agent_ip不合法'}, status=400)
        category = 'tapp' if mode == 'test' else 'app'
        defaults = {'is_active': False,'app_port': web_port,'ip_port': agent_port,'category': category}
        bk_server, _ = models.BkServer.objects.update_or_create(ip_address=agent_ip, defaults=defaults)
        active_server = models.BkServer.objects.filter(category=category, is_active=True)
        if active_server:
            err_msg = u'%s环境已有一台服务器%s处于激活中, 无法再激活%s' % (mode, active_server[0].ip_address, agent_ip)
            return JsonResponse({'msg': err_msg})
        return JsonResponse({'sid': bk_server.s_id,'token': bk_server.token})

    def get(self, request):
        agent_ip = request.GET.get('agent_ip', '')
        try:
            if not agent_ip:
                return JsonResponse({'msg': u'agent_ip不能为空'}, status=400)
            ip_pattarn = re.compile('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')
            if not ip_pattarn.match(agent_ip):
                return JsonResponse({'msg': u'agent_ip不合法'}, status=400)
            bk_server = models.BkServer.objects.get(ip_address=agent_ip)
            _, result = http.http_get(('http://{}:{}/v1/app/healthz').format(bk_server.ip_address, bk_server.ip_port), bk_server.s_id, bk_server.token, {})
            if isinstance(result, dict) and result.get('error') == 0:
                bk_server.is_active = True
                bk_server.mac = result.get('mac', '')
                bk_server.save()
                return JsonResponse({'agent_ip': bk_server.ip_address})
            return JsonResponse({'msg': 'code=1'})
        except Exception as e:
            return JsonResponse({'msg': '%s' % e}, status=400)


class MqRegistryView(View):

    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        return super(MqRegistryView, self).dispatch(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        mq_ip = request.POST.get('mq_ip', '')
        if not mq_ip:
            return JsonResponse({'msg': u'mq_ip不能为空'}, status=400)
        ip_pattarn = re.compile('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')
        if not ip_pattarn.match(mq_ip):
            return JsonResponse({'msg': u'mq_ip不合法'}, status=400)
        mq_port = '15672'
        server_info = {'server_ip': mq_ip,
           'server_port': mq_port
           }
        username = request.POST.get('username', 'admin')
        password = request.POST.get('password', 'admin')
        admin_account = {'name': username,
           'password': password
           }
        try:
            ret, data = check_rabbitmq(server_info, admin_account)
            category = 'rabbitmq'
            if ret:
                active_server = models.ThirdServer.objects.filter(category=category, is_active=True)
                if active_server:
                    err_msg = u'已有一台rabbitmq服务器处于激活中, 无法再激活%s' % mq_ip
                    return JsonResponse({'msg': err_msg})
                server_info = {'ip_address': mq_ip,'ip_port': mq_port,
                   'username': username,
                   'password': password
                   }
                server_info = json.dumps(server_info)
                defaults = {'server_info': server_info,
                   'is_active': True
                   }
                models.ThirdServer.objects.update_or_create(category=category, defaults=defaults)
                return JsonResponse({'mq_ip': mq_ip})
            return JsonResponse({'msg': data})
        except Exception as e:
            return JsonResponse({'msg': '%s' % e}, status=400)
